GraphPaper = setmetatable({ }, { __index = _G })
setfenv(1, GraphPaper)

addon = GraphPaper
title = "GraphPaper"

local select, unpack = select, unpack

--[[-----------------------------------------------------------------------------
Local to module
-------------------------------------------------------------------------------]]
local HEIGHT_FACTOR, SQUARE_FACTOR, WIDTH_FACTOR = 1, 1, 1
local NATIVE_HEIGHT, NATIVE_WIDTH, NATIVE_WIDE = { [false] = 768, [true] = 768 }, { [false] = 1024, [true] = 1024 }, false
local drawnLines, erasedLines, grid = { }, { }, CreateFrame('Frame', nil, UIParent)
local LDA = LibStub('LibDisplayAssist-1.2')
grid:SetAllPoints(UIParent)

local centerLineX, centerLineY = grid:CreateTexture(nil, 'BORDER'), grid:CreateTexture(nil, 'BORDER')
centerLineX:SetPoint('TOP', grid, 'TOP')
centerLineX:SetPoint('BOTTOM', grid, 'BOTTOM')
centerLineX:SetTexture([[Interface\BUTTONS\WHITE8X8]])
centerLineY:SetPoint('LEFT', grid, 'LEFT')
centerLineY:SetPoint('RIGHT', grid, 'RIGHT')
centerLineY:SetTexture([[Interface\BUTTONS\WHITE8X8]])

local function DrawLine(axis, offset)
	local line
	erasedLines[#erasedLines], line = nil, erasedLines[#erasedLines]
	if not line then
		line = grid:CreateTexture(nil, 'BACKGROUND')
		line:SetTexture([[Interface\BUTTONS\WHITE8X8]])
		line:SetVertexColor(unpack(settings.gridColor))
	end
	line:ClearAllPoints()
	if axis == "x" then
		line:SetPoint('TOP', grid, 'TOP', offset, 0)
		line:SetPoint('BOTTOM', grid, 'BOTTOM', offset, 0)
		line:SetWidth(settings.gridThickness * WIDTH_FACTOR)
	else
		line:SetPoint('LEFT', grid, 'LEFT', 0, offset)
		line:SetPoint('RIGHT', grid, 'RIGHT', 0, offset)
		line:SetHeight(settings.gridThickness * HEIGHT_FACTOR)
	end
	drawnLines[#drawnLines + 1] = line
	line:Show()
end

local function DrawLines(axis, start, stop, step)
	for offset = start, stop, step do
		DrawLine(axis, offset)
		DrawLine(axis, -offset)
	end
end

local function EraseLines()
	local numErased = #erasedLines
	for index = #drawnLines, 1, -1 do
		numErased = numErased + 1
		erasedLines[numErased] = drawnLines[index]
		drawnLines[index]:Hide()
		drawnLines[index] = nil
	end
end

local function UpdateProfile()
	settings = addon.db.profile
	UpdateCenterLines()
	ColorGrid()
	DrawGrid()
	UpdateState()
end

SCREEN_HEIGHT, SCREEN_WIDTH = 768, 1024
local UpdateResolution
do
	local function update_wide(...)
		local width, height, wideScreen
		for index = 1, select('#', ...) do
			width, height, wideScreen = LDA:GetResolutionInfo(select(index, ...))
			NATIVE_HEIGHT[wideScreen], NATIVE_WIDTH[wideScreen], NATIVE_WIDE = height, width, wideScreen
		end
	end

	function UpdateResolution(event, screenWidth, screenHeight, resolutionWidth, resolutionHeight, wideScreen)
		HEIGHT_FACTOR, WIDTH_FACTOR = screenHeight / resolutionHeight, screenWidth / resolutionWidth
		SCREEN_HEIGHT, SCREEN_WIDTH = screenHeight, screenWidth
		update_wide(GetScreenResolutions())
		SQUARE_FACTOR = (resolutionHeight / resolutionWidth) / (NATIVE_HEIGHT[wideScreen and NATIVE_WIDE] / NATIVE_WIDTH[wideScreen and NATIVE_WIDE])
		if GetCVarBool('gxWindow') and not GetCVarBool('gxMaximize') then
			SQUARE_FACTOR = 1
		end
		UpdateCenterLines()
		DrawGrid()
	end
end

--[[-----------------------------------------------------------------------------
Global to addon
-------------------------------------------------------------------------------]]
function ColorGrid()
	local red, green, blue, alpha = unpack(settings.gridColor)
	for index = 1, #drawnLines do
		drawnLines[index]:SetVertexColor(red, green, blue, alpha)
	end
	for index = 1, #erasedLines do
		erasedLines[index]:SetVertexColor(red, green, blue, alpha)
	end
end

function DrawGrid()
	local spacingX, spacingY, squareFactor = settings.spacingX
	if settings.independent then
		spacingY = settings.spacingY
		squareFactor = 1
	else
		spacingY = spacingX
		squareFactor = settings.squaring and SQUARE_FACTOR or 1
	end
	EraseLines()
	if settings.absolute then
		DrawLines("x", spacingX, SCREEN_WIDTH / 2, spacingX)
		spacingY = spacingY * squareFactor
		DrawLines("y", spacingY, SCREEN_HEIGHT / 2, spacingY)
	else
		DrawLines("x", spacingX + settings.centerThickness - 1, SCREEN_WIDTH / 2, spacingX + settings.gridThickness - 1)
		DrawLines("y", (spacingY + settings.centerThickness - 1) * squareFactor, SCREEN_HEIGHT / 2, (spacingY + settings.gridThickness - 1) * squareFactor)
	end
end

function UpdateCenterLines()
	centerLineX:SetWidth(settings.centerThickness * WIDTH_FACTOR)
	centerLineX:SetVertexColor(unpack(settings.centerColor))
	centerLineY:SetHeight(settings.centerThickness * HEIGHT_FACTOR)
	centerLineY:SetVertexColor(unpack(settings.centerColor))
end

function UpdateState()
	if settings.hide then
		grid:Hide()
	else
		grid:Show()
	end
	if settings.overlay then
		grid:SetFrameStrata('TOOLTIP')
		grid:SetFrameLevel(65535)
	else
		grid:SetFrameStrata('BACKGROUND')
		grid:SetFrameLevel(0)
	end
end

--[[-----------------------------------------------------------------------------
Initialize
-------------------------------------------------------------------------------]]
LDA:Register(addon, function(...)																-- Runs on first frame update
	LDA:Unregister(addon)
	addon.db = LibStub('AceDB-3.0'):New(title .. "Settings", { profile = DEFAULT_SETTINGS }, 'Default')
	UpdateProfile()
	UpdateResolution(...)
	SetupConfig()
	addon.db.RegisterCallback(addon, 'OnProfileChanged', UpdateProfile)
	addon.db.RegisterCallback(addon, 'OnProfileCopied', UpdateProfile)
	addon.db.RegisterCallback(addon, 'OnProfileReset', UpdateProfile)
	LDA:Register(addon, UpdateResolution)
	_G.CONFIGMODE_CALLBACKS = _G.CONFIGMODE_CALLBACKS or { }
	_G.CONFIGMODE_CALLBACKS[title] = function(action)
		if action == 'ON' then
			settings.hide = false
		elseif action == 'OFF' then
			settings.hide = true
		else
			return
		end
		UpdateState()
		UpdateConfig()
	end
end)
